﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace GrapeCity.ColinHan.Reflactor
{
    public class AssemblyLoader
    {
        #region Instance Data

        private readonly Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();
        public IEnumerable<string> ReferenceSearchPath { get; set; }

        #endregion

        public Assembly LoadAssembly(string filePath)
        {
            Assembly assembly = null;
            if (this._assemblies.TryGetValue(filePath, out assembly))
            {
                return assembly;
            }

            assembly = Assembly.LoadFrom(filePath);
            this._assemblies[filePath] = assembly;

            AssemblyName[] referenceAssemblyNames = assembly.GetReferencedAssemblies();
            foreach (AssemblyName @ref in referenceAssemblyNames)
            {
                if (!AppDomain.CurrentDomain.GetAssemblies().Any(loaded=>loaded.GetName().FullName == @ref.FullName))
                {
                    Assembly referencyAssembly;
                    try
                    {
                        referencyAssembly = Assembly.Load(@ref);
                    }
                    catch
                    {
                        if (!TryLoadAssembly(@ref))
                        {
                            throw new IOException(string.Format("File '{0}.dll' is not found.", @ref.Name));
                        }
                    }
                }
            }

            return assembly;
        }

        private bool TryLoadAssembly(AssemblyName referenceAssemblyName)
        {
            foreach (var searchPath in ReferenceSearchPath)
            {
                string referencyAssemblyFilePath = Path.Combine(searchPath,
                                                                referenceAssemblyName.Name + ".dll");
                try
                {
                    LoadAssembly(referencyAssemblyFilePath);
                    return true;
                }
                catch
                { }
            }
            return false;
        }
    }
}
